/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 fire_ring.cpp

	$Header: /game/fire_ring.cpp $

	$NoKeywords: $

 ************************************************************************/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "fire_ring.h"

#include "battlefield.h"
#include "can_cast.h"
#include "combat_creature.h"
#include "combat_spell_registration.h"
#include "direction.h"
#include "external_string.h"
#include "magic_mirror.h"
#include "spell_actor_animation.h"

static t_combat_spell_registration<t_fire_ring>
       k_fire_ring_register( k_spell_fire_ring );
static int const k_empty_diameter = 3;
static int const k_effect_distance = 3;

// --------------------------------------------------------------------
// handle fire ring spell
// --------------------------------------------------------------------
t_fire_ring::t_fire_ring( t_battlefield& battlefield, t_spell spell )
           : t_combat_spell( battlefield, spell )
{
}

// --------------------------------------------------------------------
// handle fire ring spell
// --------------------------------------------------------------------
bool t_fire_ring::begin_casting()
{
	return true;
}

// --------------------------------------------------------------------
// handle fire ring spell
// --------------------------------------------------------------------
bool t_fire_ring::can_cast( t_screen_point const& point ) const
{
	t_combat_creature_ptr  creature;
	t_map_point_2d		   map_point;
	t_combat_creature_list targets;

	return can_cast( point, creature, map_point, targets, 0 );
}

bool t_fire_ring::cast_on( t_combat_creature_ptr caster, 
		                   t_combat_creature_ptr target, 
						   int power,
						   bool is_mirror )
{
	int damage;
	t_combat_action_message message( *caster, get_action_text( is_mirror ) );

	damage = target->modify_spell_damage( caster, power, m_spell );
	target->add_damage( damage, caster, false, false, message );
	target->check_flinch( message );
	target->apply_damage( m_battlefield, true );
	return true;
}


// --------------------------------------------------------------------
// handle fire ring spell
// --------------------------------------------------------------------
static t_combat_actor_model_cache_set k_fireball_model( "spells.fire ring" );

void t_fire_ring::execute( t_combat_creature& caster )
{
	t_combat_creature_list::iterator index = m_targets.begin();
	t_combat_creature_list::iterator end = m_targets.end();
	int								 radius;
	int								 power;

	pay_cost();
	power = m_caster->get_spell_power( m_spell );
	play_sound();
	for (; index != end; index++)
	{
		cast_on( m_caster, *index, power, false );
		if (index->get()->has_magic_mirror())
			m_battlefield.add_action( new t_mirror_spell_action( *index, this, power ) );
	}

	// create animations
	int						 fireball_count;
	t_combat_actor_ptr		 fireball;
	int				         circumference;
	t_combat_actor_model_ptr model;
	int						 half_fireball_footprint_size;
	t_map_point_2d			 half_fireball_footprint;
	t_map_point_2d			 center;

	// create a fireball to get the proper radius
	model = k_fireball_model.get( m_battlefield.get_model_scale() );
	fireball = new t_unsaved_combat_actor( &m_battlefield, model, k_combat_actor_action_cast_spell, 
											k_direction_northeast );
	half_fireball_footprint_size = fireball->get_half_footprint_size();
	// radius where fireballs is placed is creature's footprint plus
	// half the fireball's footprint
	if (m_target == 0)
	{
		center = get_cell_center( m_target_square );
		radius = (k_empty_diameter * k_battlefield_subcells_per_cell) >> 1;
	}
	else
	{
		center = m_target->get_footprint_center();
		radius = m_target->get_half_footprint_size();
	}
	radius += half_fireball_footprint_size;
	half_fireball_footprint.row = half_fireball_footprint_size;
	half_fireball_footprint.column = half_fireball_footprint_size;
	// determine size of ring where fireballs are placed
	circumference = radius * 2.0 * k_pi;
	// resulting count is the size of the ring divided by the footprint size
	fireball_count = (circumference / fireball->get_footprint_size()) 
		             >> k_battlefield_subcell_shift;

	int            i;
	double         angle;
	t_map_point_2d point;
	t_combat_action_message message( caster, get_action_text( false ) );
	t_spell_actor_animation_ptr animation;

	for (i = 0; i < fireball_count; i++)
	{
		angle = i * 2 * k_pi / fireball_count + k_pi / 4;
		point.row = sin( angle ) * radius;
		point.column = cos( angle ) * radius;
		point += center;
		point -= half_fireball_footprint;
		if (m_battlefield.off_map( *fireball, get_cell(point) ))
			continue;
		m_battlefield.place_object( fireball, point );
		animation = new t_spell_actor_animation( m_battlefield, *fireball, this, t_handler() );
		animation->display_action_message( message );
		fireball->set_animation( animation );
		fireball = new t_unsaved_combat_actor( &m_battlefield, model, k_combat_actor_action_cast_spell, 
												k_direction_northeast );
	}
	m_battlefield.set_spell( 0 );
}


void t_fire_ring::execute_mirror_spell( t_combat_creature_ptr creature, int power )
{
	play_sound();
	cast_on( creature, m_caster, power, true );

	// create animations
	t_combat_actor_ptr			fireball;
	t_combat_actor_model_ptr	model;
	int							half_fireball_footprint_size;
	t_map_point_2d				point;
	t_combat_action_message		message( *creature, get_action_text( true ) );
	t_spell_actor_animation_ptr	animation;

	// create a fireball to get the proper radius
	model = k_fireball_model.get( m_battlefield.get_model_scale() );
	fireball = new t_unsaved_combat_actor( &m_battlefield, model,
											k_combat_actor_action_cast_spell, 
											k_direction_northeast );
	half_fireball_footprint_size = fireball->get_half_footprint_size();
	point = m_caster->get_footprint_center();;
	point.row -= half_fireball_footprint_size;
	point.column -= half_fireball_footprint_size;
	m_battlefield.place_object( fireball, point );
	animation = new t_spell_actor_animation( m_battlefield, *fireball, this, t_handler() );
	animation->display_action_message( message );
	fireball->set_animation( animation );
}

// --------------------------------------------------------------------
// Generate the list of casting actions the given caster could take
// for this spell
// --------------------------------------------------------------------
t_combat_ai_action_list t_fire_ring::generate_combat_ai_action_list(t_combat_ai &owner)
{
	// Not implemented
	return t_combat_ai_action_list();
}

double t_fire_ring::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0;
}

// --------------------------------------------------------------------
// handle fire ring spell
// --------------------------------------------------------------------
bool t_fire_ring::left_click( t_screen_point const& point )
{
	if (!can_cast( point, m_target, m_target_square, m_targets, 0 ))
		return false;
	if (m_target == 0)
		m_battlefield.begin_spell( m_target_square );
	else
		m_battlefield.begin_spell( m_target );
	return true;
}

// --------------------------------------------------------------------
// handle fire ring spell
// --------------------------------------------------------------------
bool t_fire_ring::can_cast( t_screen_point const&   point,
						    t_combat_creature_ptr&  creature,
							t_map_point_2d       &  map_point,
							t_combat_creature_list& targets,
							std::string*		    help_text ) const
{
	creature = m_battlefield.creature_hit_test( point );
	if (creature == 0)
	{
		if (!m_battlefield.cell_hit_test( point, map_point))
			return false;
		if (!check_line_of_sight( *m_caster, m_spell, map_point, help_text ))
			return false;
		targets = get_targets( map_point );
	}
	else
	{
		if (!check_line_of_sight( *m_caster, m_spell, *creature, help_text ))
			return false;
		targets = get_targets( creature );
	}
	return contains_enemies( *m_caster, m_spell, targets, help_text );
}

// --------------------------------------------------------------------
// handle fire ring spell
// --------------------------------------------------------------------

t_mouse_window* t_fire_ring::mouse_move( t_screen_point const& point,
										 std::string& help_text )
{
	t_combat_creature_ptr	creature;
	t_mouse_window*			result;
	t_combat_creature_list  targets;
	t_map_point_2d			map_point;

	result = m_battlefield.get_blocked_cursor();
	if (!can_cast( point, creature, map_point, targets, &help_text ))
	{
		m_battlefield.select_target( creature );
		return m_battlefield.get_blocked_cursor();
	}
	m_battlefield.select_targets( targets );
	return m_battlefield.get_spell_cursor();
}

// --------------------------------------------------------------------
// Get targets from spell
// --------------------------------------------------------------------
t_combat_creature_list t_fire_ring::get_targets( t_map_point_2d const& center,
												 t_combat_creature*    subject,
												 int				   footprint_size ) const
{
	t_combat_creature_list			 targets;
	t_combat_creature_list::iterator index = m_battlefield.creatures_begin();
	t_combat_creature_list::iterator end = m_battlefield.creatures_end();
	t_combat_creature*				 creature;
	int								 radius;

	// get target's footprint
	radius = footprint_size << (k_battlefield_subcell_shift - 1);
	// add effect radius
	radius += k_effect_distance * k_battlefield_subcells_per_cell;
	// find who is actually in the area
	for (; index != end; index++)
	{
		creature = index->get();

		if (creature->get_number() <= 0)
			continue;

		if (creature->get_edge_subcell_distance( center ) >= radius)
			continue;

		if (creature == subject)
			continue;

		if (!can_affect( *m_caster, m_spell, *creature ))
			continue;

		targets.push_back( creature );
	}
	return targets;
}

// --------------------------------------------------------------------
// Get targets from spell
// --------------------------------------------------------------------
t_combat_creature_list t_fire_ring::get_targets( t_combat_creature* subject ) const
{
	t_map_point_2d					 center = subject->get_footprint_center();

	return get_targets( center, subject, subject->get_footprint_size() );
}

// --------------------------------------------------------------------
// Get targets from spell
// --------------------------------------------------------------------
t_combat_creature_list t_fire_ring::get_targets( t_map_point_2d const& cell ) const
{
	t_map_point_2d center = get_cell_center( cell );

	return get_targets( center, 0, k_empty_diameter );
}



// --------------------------------------------------------------------
// Get targets from spell
// Static function
// --------------------------------------------------------------------
void t_fire_ring::get_basic_target_list( t_battlefield&				battlefield,
										 t_map_point_2d const   &center,
									     const t_combat_creature      *subject,
									     int				     footprint_size,
									     t_combat_creature_list &result )
{
	t_combat_creature_list::iterator index = battlefield.creatures_begin();
	t_combat_creature_list::iterator end = battlefield.creatures_end();
	t_combat_creature*				 creature;
	int								 radius;

	result.clear();

	// get target's footprint
	radius = footprint_size << (k_battlefield_subcell_shift - 1);
	// add effect radius
	radius += k_effect_distance * k_battlefield_subcells_per_cell;
	// find who is actually in the area
	for (; index != end; index++)
	{
		creature = index->get();

		if (creature->get_number() <= 0)
			continue;

		if (creature->get_edge_subcell_distance( center ) >= radius)
			continue;

		if (creature == subject)
			continue;

		result.push_back( creature );
	}
}


// --------------------------------------------------------------------
// Get targets from spell
// Static function
// --------------------------------------------------------------------
void t_fire_ring::get_basic_target_list( t_battlefield& battlefield,
										 t_map_point_2d const& cell,
										 t_combat_creature_list &result )
{
	t_map_point_2d center = get_cell_center( cell );
	get_basic_target_list( battlefield, center, 0, k_empty_diameter, result);
}


// --------------------------------------------------------------------
// Get targets from spell
// Static function
// --------------------------------------------------------------------
void t_fire_ring::get_basic_target_list( t_battlefield& battlefield,
										 const t_combat_creature* subject, 
										 t_combat_creature_list &result )
{
	t_map_point_2d center = subject->get_footprint_center();
	get_basic_target_list( battlefield, center, subject, subject->get_footprint_size(), result);
}

